<?php

namespace Agi\Config;

/**
 * 全局容器
 *
 * 注意：如果可以的，请使用具体的 Config 实现类和方法，而不是直接调用该类的方法。
 *
 * <code>
 * use Agi\Config\Db;
 * use Agi\Config\Mail;
 *
 * // 数据库配置
 * // 添加一个名为 mysql 配置
 * Db::define('mysql', array(
 *     'adapter' => Db::MYSQL_PDO,
 *     'host' => 'any-host',
 * ));
 *
 * // 添加多个配置
 * Db::defineMulti(array(
 *     // 全局默认的配置
 *     array(
 *         'adapter' => Db::MYSQL_PDO,
 *         'host' => 'any-host',
 *     ),
 * // 添加一个名为 pgsql 的数据库
 *     'pgsql' => array(
 *         'adapter' => Db::PGSQL_PDO,
 *         'host' => 'any-host',
 *     ),
 * ));
 *
 * // 邮箱配置
 * // 第一个参数 $name 为空，表示为全局默认配置
 * Mail::define(null, array(
 *     'host' => 'smtp.gmail.com',
 *     'smtp' => Mail::USE_SSL,
 *     'username' => 'any@gmail.com',
 *     'password' => 'any-password',
 * ));
 *
 * // 取出默认的配置 default
 * $db = Db::get();
 * $pgsql = Db::get('pgsql');
 *
 * // 输出配置的信息
 * var_dump($db->host, $db->port, $db->name);
 * var_dump($pgsql->isPdo, $pgsql->isNative);
 * // 如此种种……
 * // 等等……
 * var_dump(Mail::get()->host);
 * var_dump(Db::get('mysql')->username);
 * </code>
 *
 * @package Agi\Config
 * @author  Janpoem created at 2015/3/11 6:10
 */
abstract class Storage
{

    /** 默认的Storage的类名，用于在addConfig时，传入的class无效时，替换为此值。 */
    const CONFIG_CLASS = 'Agi\\Config\\Config';

    /** @var array 已知明确有效的类名，存储格式：className => true */
    private static $knownClasses = array(
        self::CONFIG_CLASS  => true,
        'Agi\\Config\\Db'   => true,
        'Agi\\Config\\Mail' => true,
    );

    /** @var array 全局ID的索引，存储格式：id => true */
    private static $ids = array();

    /** @var array 全局Config实例的存储容器，存储格式：id => StorageImpl */
    private static $configs = array();

    /** @var array 全局Config数据的存储容器，存储格式：id => array */
    private static $data = array();

    /**
     * 添加配置数据
     *
     * 该方法只添加配置数据，并不会将配置的数据转化成为一个 Config 的实例。
     *
     * @param string      $class 指定容器类名
     * @param null|string $name  访问名
     * @param array       $data  配置数据
     *
     * @return bool 是否成功添加
     */
    public static function addData($class = null, $name = null, array $data = array())
    {
        if (empty($class) || !isset(self::$knownClasses[$class]) ||
            !class_exists($class, true) || !is_subclass_of($class, Impl::class)
        ) {
            $class = Config::class;
        }

        /** @var Impl $class */
        if (empty($name))
            $name = $class::DEFAULT_NAME;

        $id = $class::mkId($name);
        if (!isset(self::$ids[$id])) {
            self::$ids[$id] = true;
            self::$data[$id] = array(
                $class,
                $name,
                $data,
            );
            return true;
        }
        return false;
    }

    /**
     * 添加配置容器实例
     *
     * @param Impl $storage
     *
     * @return bool 是否成功添加
     */
    public static function addConfig(Impl $storage)
    {
        $id = $storage->getId();
        if (!isset(self::$ids[$id])) {
            self::$ids[$id] = true;
            self::$configs[$id] = $storage;
            return true;
        }
        return false;
    }

    /**
     * 判断是否存在指定id的配置（包括配置实例或配置数据）
     *
     * @param string $id
     *
     * @return bool 是否存在配置
     */
    public static function has($id)
    {
        return isset(self::$ids[$id]);
    }

    /**
     * 取得配置实例
     *
     * @param string $id
     *
     * @return Impl 配置容器实例
     */
    public static function getConfig($id)
    {
        if (isset(self::$configs[$id]))
            return self::$configs[$id];
        if (isset(self::$data[$id])) {
            /**
             * @var Impl   $class
             * @var string $name
             * @var array  $data
             */
            list($class, $name, $data) = self::$data[$id];
            self::$configs[$id] = new $class($name, $data);
            unset(self::$data[$id]);
//            static::addStorage(new $class($name, $data));
            return self::$configs[$id];
        }
        return false;
    }
}
